#include <new>
//#include <stdlib.h>
#include "CppMemPool.h"
#include "BaseObject.h"

namespace cppflib
{

namespace cppmempool
{


_DLLAPI bool GlobalInit()
{
    return ::MRP_GlobalInit() ? true : false;
}

_DLLAPI bool GlobalFinish()
{
    return ::MRP_GlobalFinish() ? true : false;
}

_DLLAPI memrelpool_t * CreatePool()
{
    return ::MRP_CreateRelPool();
}

_DLLAPI void FreePool(memrelpool_t * pRelPool)
{
    ::MRP_FreePool(pRelPool);
}

_DLLAPI bool UnlinkFromGroup(memrelpool_t * pRelPool)
{
    return ::MRP_UnlinkPoolFromGroup(pRelPool) ? true : false;
}

_DLLAPI bool AddAsCurrentPool(memrelpool_t * pRelPool)
{
    return ::MRP_AddPoolAsCurrentPool(pRelPool) ? true : false;
}

_DLLAPI int GetPoolNodesCount(memrelpool_t * pRelPool)
{
    return ::MRP_GetPoolNodesCount(pRelPool);
}

_DLLAPI int GetPoolsCount()
{
    return ::MRP_GetPoolsCount();
}

_DLLAPI int Release(void * pMemLoc)
{
    return ::MRP_Release(pMemLoc);
}

_DLLAPI int Retain(void * pMemLoc)
{
    return ::MRP_Retain(pMemLoc);
}

_DLLAPI void * UnlinkFromPool(void * pMemLoc)
{
    return ::MRP_UnlinkFromPool(pMemLoc);
}

_DLLAPI bool AddToPool(void * pMemLoc, memrelpool_t *pRelPool)
{
    return ::MRP_AddToPool(pMemLoc, pRelPool) ? true : false;
}

_DLLAPI bool AddToCurPool(void * pMemLoc)
{
    return ::MRP_AddToCurPool(pMemLoc) ? true : false;
}

_DLLAPI bool AddToDefaultPool(void * pMemLoc)
{
    return ::MRP_AddToDefaultPool(pMemLoc) ? true : false;
}

} // end of namespace cppmempool

} // end of namespace cppflib


/*********************************************************************/


/**
 *  Custom destroy function for BaseObject derived class instance
 */
#if defined(_DEBUG)
extern "C"
#else
PRIVATE
#endif
void __CMP_DestroyBaseObject(void *p)
{
   cppflib::BaseObject *tp = reinterpret_cast<cppflib::BaseObject*>(p);
   tp->~BaseObject(); // call destructor explicitly, it will be freed later in MRP_Release
}

#if defined(WIN32) && defined(_DEBUG)
/**
 *  An ugly hack to get the destructor address to help find out which object is leaking
 */
extern "C" void * __CMP_GetDestructorAddress(void *p)
{
   // vtable address 
   ptr_t * vtableAddress = reinterpret_cast<ptr_t*>(p);  
   // fetch vtable starting point
   ptr_t * vtable = reinterpret_cast<ptr_t*>(*vtableAddress); 
   // first entry is destructor according to declaration of BaseObject
   return reinterpret_cast<void*>(vtable[0]);
}

#endif

_DLLAPI void * operator new(size_t size, cppflib::cppmempool::cmparg_t cppmemsig)
{
   void *pRet = NULL;

   if (cppmemsig == cppflib::cppmempool::NEWCLASS) {
      // provide a custom dtor function
      pRet = ::MRP_CallocARelDtor(1, static_cast<sz_t>(size), &__CMP_DestroyBaseObject);
   }
   else if (cppmemsig == cppflib::cppmempool::NEWPTR) {
      // others
      pRet = ::MRP_CallocARel(1, static_cast<sz_t>(size));
   }

   return pRet;
}

/**
 *  Only support array of primitive types (e.g. int, char, ...)
 */
_DLLAPI void * operator new[](size_t size, cppflib::cppmempool::cmparg_t cppmemsig)
{
   void *pRet = NULL;
   
   if (cppmemsig == cppflib::cppmempool::NEWPTR)
      pRet = ::MRP_CallocARel(1, static_cast<sz_t>(size));
   else
      throw "Can't allocate array of non-primitive types";

   return pRet;
}

/**
 *  For memory blocks wish to have some cleanup job before being freed
 */
_DLLAPI void * operator new(size_t size, dtorfunc_t dtorFunc)
{
   void *pRet = ::MRP_CallocARelDtor(1, static_cast<sz_t>(size), dtorFunc);
   return pRet;
}


_DLLAPI void operator delete(void *p, cppflib::cppmempool::cmparg_t cppmemsig)
{
   ::MRP_Release(p);
}

_DLLAPI void operator delete[](void *p, cppflib::cppmempool::cmparg_t cppmemsig)
{
   ::MRP_Release(p);
}

_DLLAPI void operator delete(void *p, dtorfunc_t dtorFunc)
{
   ::MRP_Release(p);
}

#ifdef _DEBUG

/**
 *  Add to default mem pool when debugging
 */

#  if defined(__GNUC__)
void * operator new(size_t size) throw (std::bad_alloc) { return ::MRP_Calloc(1, size); }
void * operator new[](size_t size) throw (std::bad_alloc) { return ::MRP_Calloc(size, sizeof(char)); }
void operator delete(void *p) throw () { ::MRP_Release(p); }
void operator delete[](void *p) throw () { ::MRP_Release(p); }
#  else  
void * operator new(size_t size) { return ::MRP_Calloc(1, size); }
void * operator new[](size_t size) { return ::MRP_Calloc(size, sizeof(char)); }
void operator delete(void *p) { ::MRP_Release(p); }
void operator delete[](void *p) { ::MRP_Release(p); }
#  endif

#endif

